using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Gko.Utils
{
	///<summary>
	///	Provides the base class for a generic sorted collection.
	///</summary>
	///<typeparam name = "TValue">The type of elements in the collection.</typeparam>
#if !SILVERLIGHT
	[Serializable]
#endif

	[DebuggerDisplay("Count = {Count}")]
	public class SortedCollection<TValue> : Collection<TValue>
	{
		private readonly IComparer<TValue> comparer;

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "T:Gko.Utils.SortedCollection`1" /> class that is empty.
		/// </summary>
		public SortedCollection()
		{
		}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "T:Gko.Utils.SortedCollection`1" /> class.
		/// </summary>
		/// <param name = "list">The list that is wrapped by the new collection.</param>
		/// <exception cref = "System.ArgumentNullException">list is null.</exception>
		public SortedCollection(ICollection<TValue> list)
			: base(Sort(list))
		{
		}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "T:Gko.Utils.SortedCollection`1" /> class that is empty.
		/// </summary>
		/// <param name = "comparer">The comparer.</param>
		public SortedCollection(IComparer<TValue> comparer)
		{
			this.comparer = comparer ?? Comparer<TValue>.Default;
		}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "T:Gko.Utils.SortedCollection`1" /> class.
		/// </summary>
		/// <param name = "list">The list.</param>
		/// <param name = "comparer">The comparer.</param>
		public SortedCollection(ICollection<TValue> list, IComparer<TValue> comparer)
			: base(Sort(list))
		{
			this.comparer = comparer ?? Comparer<TValue>.Default;
		}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "T:Gko.Utils.SortedCollection`1" /> class that is empty.
		/// </summary>
		/// <param name = "comparer">The comparer.</param>
		public SortedCollection(Func<TValue, TValue, int> comparer)
			: this(new ComparerWrapper<TValue>(comparer))
		{
		}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "T:Gko.Utils.SortedCollection`1" /> class.
		/// </summary>
		/// <param name = "list">The list.</param>
		/// <param name = "comparer">The comparer.</param>
		public SortedCollection(ICollection<TValue> list, Func<TValue, TValue, int> comparer)
			: this(list, new ComparerWrapper<TValue>(comparer))
		{
		}

		/// <summary>
		/// 	Sorts the specified generic list.
		/// </summary>
		/// <param name = "genericList">The generic list.</param>
		/// <returns>Sorted generic list.</returns>
		/// <exception cref = "ArgumentNullException"><c>genericList</c> is null.</exception>
		private static IList<TValue> Sort(ICollection<TValue> genericList)
		{
			if (genericList == null) {
				throw new ArgumentNullException("genericList");
			}
			var sorted = genericList as SortedCollection<TValue>;
			if (sorted != null) {
				var values = new TValue[genericList.Count];
				sorted.CopyTo(values, 0);
				return values.ToList();
			}
			var list = genericList.ToList();
			list.Sort();
			return list;
		}

		/// <summary>
		/// 	Inserts an element into the <see cref = "T:System.Collections.ObjectModel.Collection`1" /> at the specified index.
		/// </summary>
		/// <param name = "index">The zero-based index at which <paramref name = "item" /> should be inserted.</param>
		/// <param name = "item">The object to insert. The value can be null for reference types.</param>
		/// <exception cref = "T:System.ArgumentOutOfRangeException"><paramref name = "index" /> is less than zero.-or-<paramref name = "index" /> is greater than <see cref = "P:System.Collections.ObjectModel.Collection`1.Count" />.</exception>
		protected override void InsertItem(int index, TValue item)
		{
			var startIndex = 0;
			var endIndex = this.Items.Count - 1;
			var found = this.Items.Find(item, ref startIndex, ref endIndex, this.comparer);

			if (found.HasValue) {
				index = found.Value + 1;
			} else {
				index = endIndex + 1;
			}

			base.InsertItem(index, item);
		}

		/// <summary>
		/// 	Replaces the element at the specified index.
		/// </summary>
		/// <param name = "index">The zero-based index of the element to replace.</param>
		/// <param name = "item">The new value for the element at the specified index. The value can be null for reference types.</param>
		/// <exception cref = "T:System.ArgumentOutOfRangeException"><paramref name = "index" /> is less than zero.-or-<paramref name = "index" /> is greater than <see cref = "P:System.Collections.ObjectModel.Collection`1.Count" />.</exception>
		protected override void SetItem(int index, TValue item)
		{
			this.RemoveAt(index);
			this.InsertItem(index, item);
		}
	}
}
